﻿using System;
using System.Collections.Generic;
using Oxage.CNC.Models;

namespace Oxage.Simulator
{
	public class StepMakerHelper
	{
		public StepMakerHelper(IStepDriver driver)
		{
			this.Driver = driver;
		}

		public IStepDriver Driver
		{
			get;
			set;
		}

		public void Arc(int radius, int startAngle, int endAngle, bool clockwise)
		{
			var arc = new ArcInline();
			var maker = new ArcStepMaker();
			arc.Radius = radius;
			arc.StartAngle = startAngle;
			arc.EndAngle = endAngle;
			arc.Clockwise = clockwise;
			maker.Run(this.Driver, arc);
		}

		public void MoveAbsolute(params double[] args)
		{
			var maker = new MoveStepMaker();
			maker.MoveAbsolute(this.Driver, new NPoint(args));
		}

		public void MoveRelative(params double[] args)
		{
			var maker = new MoveStepMaker();
			maker.MoveRelative(this.Driver, new NPoint(args));
		}

		//Dictionary<interface, maker>
		protected Dictionary<Type, Type> FindInlineBindings()
		{
			//TODO: InlineBinding priority, get from attribute
			var result = new Dictionary<Type, Type>();
			result.Add(typeof(IArcInline), typeof(ArcStepMaker));
			result.Add(typeof(ILineInline), typeof(LineStepMaker));
			result.Add(typeof(IMoveInline), typeof(MoveStepMaker));
			result.Add(typeof(IBezierInline), typeof(BezierStepMaker));
			return result;
		}

		public void Sequence(IEnumerable<IMachineCodeInline> inlines)
		{
			var bindings = FindInlineBindings();
			foreach (var inline in inlines)
			{
				var type = inline.GetType();
				foreach (var binding in bindings)
				{
					if (binding.Key.IsAssignableFrom(type))
					{
						var maker = Activator.CreateInstance(binding.Value) as IStepMaker;
						maker.Run(this.Driver, inline);
					}
				}
			}
		}

		public void RunGCode(string code)
		{
			var inlines = GetGCodeInlines(code);
			Sequence(inlines);
		}

		protected IEnumerable<IMachineCodeInline> GetGCodeInlines(string code)
		{
			var serializer = new GCodeSerializer();
			var obj = serializer.Deserialize(code);
			foreach (var block in obj.Blocks)
			{
				foreach (var inline in block.Inlines)
				{
					yield return inline;
				}
			}
		}
	}
}
